package ca.ubc.cpsc210.waldo.tests;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;

import org.junit.Test;

import ca.ubc.cpsc210.waldo.model.Bus;
import ca.ubc.cpsc210.waldo.model.BusRoute;
import ca.ubc.cpsc210.waldo.model.BusStop;
import ca.ubc.cpsc210.waldo.translink.TranslinkService;
import ca.ubc.cpsc210.waldo.translink.TranslinkServiceFromJSONFile;
import ca.ubc.cpsc210.waldo.translink.TranslinkServiceFromXMLFile;

public class BusTests {

	// The service to test
	private TranslinkService service;

	// Files used in testing
	private final String TESTDATA = "TestFiles/Student/BusEstimatesFor61598";

	/**
	 * Test estimates for stop 61598
	 */
	@Test
	public void testReadInBusEstimatesInfo() {
		service = new TranslinkServiceFromXMLFile();
		try {
			FileReader input = new FileReader(TESTDATA + ".xml");

			// Initialize routes
			List<BusRoute> routes = new ArrayList<BusRoute>();
			routes.add(new BusRoute("025"));
			routes.add(new BusRoute("033"));
			routes.add(new BusRoute("041"));
			routes.add(new BusRoute("043"));
			routes.add(new BusRoute("049"));
			routes.add(new BusRoute("480"));
			service.addToRoutes(routes);

			service.getBusEstimtesForStop(new BusStop(61598), input);

			Set<BusRoute> expectedRoutes = createBusRoutesExpected(TESTDATA
					+ ".txt", new BusStop(61598));

			Set<Bus> expectedBuses = createBusesExpected(expectedRoutes);
			assertEquals(expectedRoutes.size(), routes.size());
			
			System.out.println("size of routes" + routes.size() + "," + expectedRoutes.size());
			
			assertEquals(expectedBuses.size(), getBusesFromRoutes(routes)
					.size());
						
			compareBusSets(expectedBuses, getBusesFromRoutes(routes));

		} catch (FileNotFoundException e) {
			fail("Unable to open testing file.");
		}
	}


	/**
	 * Compare the expected and parsed buses
	 * 
	 * @param expectedBuses The oracle data
	 * @param busesFromRoutes The parsed data
	 */
	private void compareBusSets(Set<Bus> expectedBuses, Set<Bus> busesFromRoutes) {
		for (Bus expectedB : expectedBuses) {
			boolean found = false;
			for (Bus parsedB : busesFromRoutes) {
				
//				System.out.println(expectedB.getDirection() + " " + parsedB.getDirection());
				
				if (!found
						&& expectedB.getDirection().equals(parsedB.getDirection())
						&& expectedB.getMinutesToDeparture() == parsedB
								.getMinutesToDeparture())
					found = true;
			}
			if (!found)
				fail("Bus sets not equal, missing: " + expectedB.getMinutesToDeparture() + " " + expectedB.getDirection());
		}
	}

	/**
	 * Extract buses from routes
	 * @param routes The routes with buses
	 * @return The buses from the routes
	 */
	private Set<Bus> getBusesFromRoutes(List<BusRoute> routes) {
		Set<Bus> buses = new HashSet<Bus>();
		for (BusRoute r : routes) {
			buses.addAll(r.getBuses());
		}
		return buses;
	}

	/**
	 * Create the expected buses
	 * @param expectedRoutes The expected routes
	 * @return The expected buses
	 */
	private Set<Bus> createBusesExpected(Set<BusRoute> expectedRoutes) {
		Set<Bus> buses = new HashSet<Bus>();
		for (BusRoute r : expectedRoutes)
			buses.addAll(r.getBuses());
		return buses;
	}

	/**
	 * Create the expected routes
	 * @param filename File with oracle data
	 * @param stop The stop the estimates are relative to
	 * @return The expected routes
	 */
	private Set<BusRoute> createBusRoutesExpected(String filename, BusStop stop) {
		BufferedReader reader = null;
		Set<BusRoute> expectedRoutes = new HashSet<BusRoute>();
		try {
			reader = new BufferedReader(new FileReader(filename));
			String line = reader.readLine();
			while (line != null) {
				StringTokenizer st = new StringTokenizer(line, ":");
				String routeNumber = st.nextToken().trim();
				BusRoute route = new BusRoute(routeNumber);
				expectedRoutes.add(route);
				String direction = st.nextToken().trim();
				String nextBusTimes = st.nextToken().trim();
				StringTokenizer st1 = new StringTokenizer(nextBusTimes, ",");
				while (st1.hasMoreTokens()) {
					int expectedTime = Integer.parseInt(st1.nextToken().trim());
					boolean cancelledTrip = convertToBoolean(st1.nextToken()
							.trim());
					if (!cancelledTrip) {
						Bus bus = new Bus(route, direction, stop, expectedTime);
						route.addBus(bus);
					}
				}
				line = reader.readLine();
			}

		} catch (IOException e) {
			fail("Unable to open oracle file: <" + filename + ">");
		} finally {
			try {
				if (reader != null)
					reader.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return expectedRoutes;
	}

	/**
	 * Convert a string to a boolean
	 * @param s The string
	 * @return true or false
	 */
	private boolean convertToBoolean(String s) {
		if (s.toLowerCase().equals("true"))
			return true;
		else
			return false;
	}

}
